#include "SignInService.h"
#include "SignIn.pb.h"
#include "CryptoUtil.h"

#include <signal.h>
#include <nlohmann/json.hpp>
#include <workflow/WFFacilities.h>
#include <workflow/MySQLResult.h>
#include <workflow/WFTaskFactory.h>
#include <ppconsul/agent.h>
#include <string>
#include <vector>

using namespace srpc;
using namespace protocol;
using namespace ppconsul::agent;
using ppconsul::Consul;
using std::string;
using std::vector;

namespace
{
	using json = nlohmann::json;

	WFFacilities::WaitGroup wait_group(1);

	//默认的注册服务器的端口
	constexpr const unsigned short DEFAULT_SIGNIN_PORT = 1235;
	//默认的注册服务器的ip
	constexpr const char* DEFAULT_SIGNIN_IP = "127.0.0.1";
	//mysql的url
	constexpr const char* MYSQL_URL = "mysql://root:123@127.0.0.1/CloudDisk2";
	//最大重试次数
	constexpr const int RE_TRY_MAX = 3;

	void sig_handler(int signo)
	{
		wait_group.done();
	}

	/* ************************************************************************************ */
		/* ********************定时发送心跳包************************************************* */
		/* ************************************************************************************ */

	void timer_callback(WFTimerTask* task)
	{
		Agent* agent = (Agent*)task->user_data;
		agent->servicePass("SignIn1");

		WFTimerTask* next = WFTaskFactory::create_timer_task(7, 0, timer_callback);
		next->user_data = task->user_data;

		series_of(task)->push_back(next);

	}



	/* ************************************************************************************ */
	/* *******************************注册************************************************* */
	/* ************************************************************************************ */

	void handleSignIn(::l::SignInResponse* response, string username, string password, WFMySQLTask* task) {

		// 任务失败: 及早失败原则
		if (task->get_state() != WFT_STATE_SUCCESS) {
			response->set_statuscode(500);
#ifdef BUG
			std::cerr << "Error: " << WFGlobal::get_error_string(task->get_state(), task->get_error());
#endif
			return;
		}

		MySQLResponse* resp = task->get_resp();
		MySQLResultCursor cursor{ resp };

		if (cursor.get_cursor_status() != MYSQL_STATUS_GET_RESULT || cursor.get_rows_count() == 0) {

			response->set_statuscode(400);
#ifdef BUG
			std::cout << "mysql failed This user does not exist" << "\n";
#endif
			return;
		}

		vector<MySQLCell> record{};
		cursor.fetch_row(record);
		string hashPassword{ record[0].as_string() };
		string salt{ record[1].as_string() };

		//验证
		if (l::CryptoUtil::verifyHashWithSalt(password, hashPassword, salt) == false) {
			response->set_statuscode(400);
#ifdef BUG
			std::cout << " Wrong password\n" << "\n";
#endif
			return;
		}


		//生成token
		string token{ l::CryptoUtil::generate_jwt_token() };
		json response_body{};
		response_body["data"] = { {"Token", token}, {"Username", username}, {"Location", "/static/view/home.html"} };
#ifdef BUG
		std::cout << "token: " << token << "\n";
		std::cout << "json:\n " << response_body.dump(2) << "\n";
#endif

		response->set_respbody(response_body.dump());
		response->set_statuscode(200);

	}


}//end of anonymous namaspace


class SignInServiceImpl : public ::l::SignIn::Service
{
public:

	void SignIn(::l::SignInRequest* request, ::l::SignInResponse* response, srpc::RPCContext* ctx) override
	{

		string username{ request->username() };
		string password{ request->password() };

		//登录，根据username查users表中的password和salt
		string url{ MYSQL_URL };
		string sql{ "SELECT password, salt FROM users WHERE username = \"" };
		sql.append(username).append("\" AND tomb = 0");
#ifdef BUG
		std::cout << "signin sql: " << sql << "\n";
#endif

		WFMySQLTask* task = WFTaskFactory::create_mysql_task(MYSQL_URL, RE_TRY_MAX, std::bind(handleSignIn, response,
			std::move(username), std::move(password), std::placeholders::_1));

		task->get_req()->set_query(sql);

		SeriesWork* series = ctx->get_series();
		series->push_back(task);

	}
};

int main()
{
	signal(SIGINT, sig_handler);

	SRPCServer server;

	SignInServiceImpl signin_impl;
	server.add_service(&signin_impl);

	if (server.start(DEFAULT_SIGNIN_PORT) == 0) {

		// 向注册中心注册服务
		Consul consul{ "http://127.0.0.1:8500", ppconsul::kw::dc = "dc1" };
		Agent agent{ consul };

		// 注册服务 
		agent.registerService(
			kw::id = "SignIn1",
			kw::name = "SignIn",
			kw::address = "127.0.0.1",
			kw::port = DEFAULT_SIGNIN_PORT,
			kw::check = TtlCheck(std::chrono::seconds{ 10 })
		);

		// 定时发送心跳包
		WFTimerTask* timerTask = WFTaskFactory::create_timer_task(7, 0, timer_callback);
		timerTask->user_data = &agent;

		timerTask->start();
		wait_group.wait();
		server.stop();
	}
	else {
#ifdef BUG
		std::cout << "SignIn server start failed\n";
#endif
	}

	google::protobuf::ShutdownProtobufLibrary();
	return 0;

}
